<template>
  <el-container class="height-100">
    <el-header class="header" height="100px">
      <el-row class="header-top" align="middle">
        <el-col class="header-item">
          监测中心
        </el-col>
      </el-row>
      <div class="grid-header">
        <div class="header-health">
          {{ weather ? currentFeatureTitle : null }} {{ weather?.info }} {{
            weather ? weather.temperature + "℃" : null
          }} {{ weather?.direct }} {{ weather?.power }}
        </div>
        <el-row class="header-bottom">
          <el-col :span="6" class="header-bottom-item">
            <el-dropdown class="drop drop-menu" popper-class="drop-menu">
              <span class="header-bottom-item">组件管理</span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item @click="statusData.mapDialog=true">地图组件</el-dropdown-item>
                  <el-dropdown-item @click="statusData.baseMapDialog=true">底图组件</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </el-col>
          <el-col :span="6" class="header-bottom-item">
            <el-dropdown class="drop drop-menu" popper-class="drop-menu">
              <span class="header-bottom-item">类型切换</span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item v-for="item in strategyMap" @click="handleLevelChange(item[0])">{{
                      item[0]
                    }}
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </el-col>
          <el-col :span="6" class="header-bottom-item">
            <el-dropdown class="drop drop-menu" popper-class="drop-menu">
              <span class="header-bottom-item">时间范围切换</span>
              <template #dropdown>
                <el-dropdown-item @click="handleTimeChange(item.value)" v-for="item in timeList">
                  {{ item.desc }}
                </el-dropdown-item>
              </template>
            </el-dropdown>
          </el-col>
          <el-col :span="6" class="header-bottom-item">
            <el-dropdown class="drop drop-menu" popper-class="drop-menu">
              <span class="header-bottom-item">分析</span>
              <template #dropdown>
                <el-dropdown-item @click="item.method()" v-for="item in searchList">
                  {{ item.desc }}
                </el-dropdown-item>
                <el-dropdown-item @click="handleInterpolate">插值</el-dropdown-item>
              </template>
            </el-dropdown>
          </el-col>
        </el-row>
        <div class="header-info">
          <div>
              <span class="header-info-item">
                {{ currentStrategy }}
              </span>
          </div>
          <div class="info-city">
              <span class="header-info-item">
                {{ currentFeatureTitle }}
              </span>
          </div>
          <div class="info-time">
            <span class="header-info-item">
              {{ timeList?.find(s => s.value == timeExchange).desc }}
            </span>
          </div>
          <div class="info-process">
            <span @click="statusData.messageDialog=true" class="header-info-item cursor">任务列表</span>
          </div>
        </div>
      </div>
      <div class="header-back"></div>
    </el-header>
    <el-container class="container">
      <el-main class="main height-100">
        <el-row class="height-100">
          <!--          左中右-->
          <el-col :span="6" class="height-100">
            <el-row justify="center" class="height-100">
              <el-col :span="23" class="grid-space height-100">
                <Echarts :classList="['echarts-mul']">
                  <!--标题-->
                  <row align="center" justify="space-between">
                    <el-text class="text padding-left-4">
                      <div class="echarts-item-pre"></div>
                      AQI实时及污染物浓度
                    </el-text>
                    <el-text class="text padding-right-4">{{
                        Common.formatTime(currentTime)
                      }}更新
                    </el-text>
                  </row>
                  <div class="echarts"/>
                  <row v-if="currentQuality" class="aqi-grid">
                    <el-text v-for="(item, key) in currentQuality" class="text">
                      {{ key.toUpperCase() }}
                      <br/>
                      <div style="height:auto;width:100%;display:inline-block;color:#000" :style="{
                        backgroundColor: key=='aqi'?Grade.getAqiByValue(currentFeature?.attributes.aqi)?.color:Grade.getGradeByLevel(currentFeature?.attributes[`${key}Level`])?.color}">
                        {{ item }}
                      </div>
                    </el-text>
                  </row>
                </Echarts>
                <Echarts :classList="['echarts-mul-2']">
                  <!--标题-->
                  <row align="center" justify="space-between">
                    <el-text class="text padding-left-4">
                      <div class="echarts-item-pre"></div>
                      空气质量预测: AQI
                    </el-text>
                    <el-text class="text padding-right-4">{{
                        Common.formatTime(currentTime)
                      }}更新
                    </el-text>
                  </row>
                  <div class="echarts"></div>
                </Echarts>
                <Echarts>
                  <div class="echarts height-100"></div>
                </Echarts>
              </el-col>
            </el-row>
          </el-col>
          <el-col :span="12" class="height-100">
            <el-row justify="center" class="height-100">
              <el-col :span="23" class="relative grid-map">
                <Echarts>
                  <div id="map">
                    <div class="animation-scalar">
                      <div class="wave1"></div>
                      <div class="wave2"></div>
                    </div>
                    <div class="animation-scalar">
                      <div class="wave1"></div>
                      <div class="wave2"></div>
                    </div>
                  </div>
                </Echarts>
                <Echarts :classList="['monitor']">
                  <!--标题-->
                  <row align="center" justify="space-between">
                    <el-text class="text padding-left-4">
                      <div class="echarts-item-pre"></div>
                      当日监测
                    </el-text>
                  </row>
                  <!--当日站点数据 出错数据 已经失效站点-->
                  <div class="echarts echarts-station">
                    <div class="station-flex">
                      <el-text class="station-number station-number-1">{{ monitorData.all }}</el-text>
                      <el-text class="station-text">站点总数</el-text>
                    </div>
                    <div class="station-flex">
                      <el-text class="station-number station-number-2">{{ monitorData.noData }}</el-text>
                      <el-text class="station-text">数据出错误站点</el-text>
                    </div>
                    <div class="station-flex">
                      <el-text class="station-number station-number-3">{{ monitorData.health }}</el-text>
                      <el-text class="station-text">优良站点</el-text>
                    </div>
                    <div class="station-flex">
                      <el-text class="station-number station-number-4">{{ monitorData.pollution }}</el-text>
                      <el-text class="station-text">污染站点</el-text>
                    </div>
                  </div>
                </Echarts>
              </el-col>
            </el-row>
          </el-col>
          <el-col :span="6" class="height-100">
            <el-row class="height-100" justify="center">
              <el-col :span="23" class="grid-space height-100">
                <Echarts :container="['echarts-hidden']" :classList="['echarts-mul-2']">
                  <!--标题-->
                  <row align="center" justify="space-between">
                    <el-text class="text padding-left-4">
                      <div class="echarts-item-pre"></div>
                      统计分析
                    </el-text>
                  </row>
                  <div class="echarts-pie">
                    <div class="echarts"></div>
                    <div class="echarts"></div>
                    <div class="pie-line"></div>
                  </div>
                </Echarts>
                <Echarts :classList="['echarts-mul-2']">
                  <!--              省市区的排名  -->
                  <!--标题-->
                  <row align="center" justify="space-between">
                    <el-text class="text padding-left-4">
                      <div class="echarts-item-pre"></div>
                      空气质量排名
                    </el-text>
                    <el-text class="text padding-right-4">{{
                        Common.formatTime(currentTime)
                      }}更新
                    </el-text>
                  </row>
                  <div class="echarts"></div>
                </Echarts>
                <Echarts>
                  <div class="echarts height-100"></div>
                </Echarts>
              </el-col>
            </el-row>
          </el-col>
        </el-row>
        <!--      可视化展示-->
        <!--      写一个扩散效果-->
        <div class="header-back"></div>
        <div class="container-back"></div>
      </el-main>
    </el-container>
    <Footer :data="footerMenu"/>
    <el-dialog @close="handleDialogClose" title="空间插值" width="580px" :close-on-click-modal="false"
               v-model="statusData.interpolateDialog" destroy-on-close>
      <el-form :model="interpolateForm" label-width="100" ref="interpolateRef">
        <el-row>
          <el-col :span="12">
            <el-form-item required label="插值方法:" prop="type">
              <el-select @change="handleInterpolateSelect" v-model="interpolateForm.type" placeholder="请选择插值方法">
                <el-option v-for="item in dict.type" :value="item.code" :label="item.value"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item required label="分辨率(米):" prop="width">
              <el-input-number v-model="interpolateForm.width" placeholder="请输入分辨率"></el-input-number>
            </el-form-item>
          </el-col>
          <el-col v-if="interpolateForm.type == 0" :span="12">
            <el-form-item required label="变异函数:" prop="model">
              <el-select v-model="interpolateForm.model" placeholder="请选择半变异函数">
                <el-option v-for="item in dict.kriging" :value="item.code" :label="item.value"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col v-if="interpolateForm.type == 0" :span="12">
            <el-form-item required label="方差参数:" prop="sigma2">
              <el-input-number v-model="interpolateForm.sigma2" placeholder="请输入方差参数"></el-input-number>
            </el-form-item>
          </el-col>
          <el-col v-if="interpolateForm.type == 0" :span="12">
            <el-form-item required label="先验方差:" prop="alpha">
              <el-input-number v-model="interpolateForm.alpha" placeholder="请输入先验方差"></el-input-number>
            </el-form-item>
          </el-col>
          <el-col v-if="interpolateForm.type == 1" :span="12">
            <el-form-item required label="距离函数:">
              <el-select v-model="interpolateForm.distance">
                <el-option v-for="item in dict.idw" :value="item.code" :label="item.value"></el-option>
              </el-select>
            </el-form-item>
            <el-form-item required label="幂函数:">
              <el-input-number v-model="interpolateForm.power" placeholder="请输入幂函数"></el-input-number>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="handleInterpolateExe">确定</el-button>
        <el-button @click="statusData.interpolateDialog = false">取消</el-button>
      </template>
    </el-dialog>
    <el-dialog v-model="statusData.messageDialog" title="任务列表" width="580px"
               :close-on-click-modal="false">
      <el-scrollbar max-height="400px">
        <process :key="processList" :data="processList"></process>
      </el-scrollbar>
    </el-dialog>
    <el-dialog v-model="statusData.baseMapDialog" title="底图切换" width="580px">
      <el-radio-group v-model="currentWebTile" @change="webTileChange">
        <el-radio v-for="item in tileList" :label="item.name" :value="item.url"></el-radio>
      </el-radio-group>
    </el-dialog>
    <el-dialog v-model="statusData.mapDialog" title="底图组件管理" width="580px">
      <el-form label-width="100px">
        <el-form-item label="图层显示器">
          <el-switch @change="handleMapView" v-model="statusData.map.mapView" active-text="开启"
                     inactive-text="关闭"></el-switch>
        </el-form-item>
        <el-form-item label="地图图例">
          <el-switch @change="handleMapLegend" v-model="statusData.map.mapLegend" active-text="开启"
                     inactive-text="关闭"></el-switch>
        </el-form-item>
        <el-form-item label="地图字段">
          <el-switch @change="handleMapField" v-model="statusData.map.mapField" active-text="开启"
                     inactive-text="关闭"></el-switch>
        </el-form-item>
      </el-form>
    </el-dialog>
  </el-container>
</template>
<script setup lang="ts">
import MapView from "@arcgis/core/views/MapView"
import ArcGISMap from "@arcgis/core/Map.js";
import {onBeforeMount, onBeforeUnmount, onMounted, ref, watch} from "vue";
import {Data, getAirQualityRealTime} from "@/api/airQuality.ts"
import WebTileLayer from "@arcgis/core/layers/WebTileLayer";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer";
import {getProvinceRealTime, getProvinces, Province, ProvinceQuality} from "@/api/province.ts";
import {Area, AreaQuality, getAreaRealTime, getAreas, Weather} from "@/api/area.ts";
import {
  AreaChartStrategy,
  AreaRender,
  Attribute,
  ChartStrategy,
  Context,
  Grade,
  ProvinceChartStrategy,
  ProvinceRender,
  Render,
  RenderChange,
  RenderType,
  StationChartStrategy,
  StationRender
} from "@/utils/render.ts";
import {Common} from "@/utils/common.ts";
import {debounce, SelfEventListener} from "@/api/common.ts";
import Graphic from "@arcgis/core/Graphic";
import * as echarts from "echarts";
import {getTime, Time} from "@/api/time.ts";
import Row from "@/components/row.vue";
import Point from "@arcgis/core/geometry/Point";
import GeoJSONLayer from "@arcgis/core/layers/GeoJSONLayer";
import SimpleFillSymbol from "@arcgis/core/symbols/SimpleFillSymbol";
import Footer, {FooterData} from "@/components/Footer.vue";
import router from "@/router/router.ts";
import SimpleRenderer from "@arcgis/core/renderers/SimpleRenderer";
import {logout, removeToken} from "@/api/login.ts";
import SimpleMarkerSymbol from "@arcgis/core/symbols/SimpleMarkerSymbol";
import Extent from "@arcgis/core/geometry/Extent";
import BaseDynamicLayer from "@arcgis/core/layers/BaseDynamicLayer";
import LayerList from "@arcgis/core/widgets/LayerList";
import {plot} from "@sakitam-gis/kriging";
import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils";
import {
  BaseMixed,
  DictMixed,
  getDict,
  postIdwInterpolate,
  postKrigingInterpolate,
  RootObject,
  searchInterpolate,
  searchInterpolateIdw
} from "@/api/interpolate.ts";
import {FormInstance} from "element-plus";
import {interpolatePoly} from "@/assets/interpolate.ts";
import {ProcessFace, ProcessManager} from "@/utils/process.ts";
import {getWeather} from "@/api/weather.ts";
import Process from "@/components/process.vue";
import HitTestResult = __esri.HitTestResult;
import GraphicHit = __esri.GraphicHit;

onBeforeMount(() => {
  initTime()
  initProcess()
})

onMounted(() => {
  init()
})

onBeforeUnmount(() => {
  removeListener()
  Common.clearAllEventListener(timer)
})

const initWorker = () => {
  worker.onmessage = (e) => {
    let {status} = e.data
    workerMessage[status](e)
  }
}

const initProcess = () => {
  ProcessManager.signal(() => {
    processList.value = {
      process: ProcessManager.getProcesses(),
      error: ProcessManager.getError(),
      success: ProcessManager.getSuccess(),
      history: ProcessManager.getHistory()
    }
    if (statusData.value.progress >= Number.MAX_VALUE) {
      statusData.value.progress = 0
    } else {
      statusData.value.progress += 1
    }
  })
}

const initResizeListener = () => {
  let f = debounce(handleResizeChange, 300)
  window.addEventListener("resize", f)
  timer.listener.push({event: "resize", func: f, userCapture: window})
}

const initTime = () => {
  getTime().then(res => {
    timeList.value = res.data.data
  }).then(initDict)
}

const initDict = () => {
  getDict().then(res => {
    dict.value = res.data.data
  })
}

const handleResizeChange = () => {
  requestAnimationFrame(() => {
    let elements = Array.from(document.getElementsByClassName("echarts"));
    for (const element of elements) {
      let instanceByDom = echarts.getInstanceByDom(element as HTMLElement);
      if (!instanceByDom) continue
      //进行增强
      instanceByDom.resize({animation: {duration: 300}})
    }
    handleRenderData(RenderChange.SizeChange)
  })
}

/**
 * 初始化
 */
const init = () => {
  initCanvas()
  initWorker()
  initMap()
  initData()
}

/**
 * 初始化地图
 */
const initMap = () => {
  //初始化视图
  view = new MapView({
    map: map,
    container: "map",
    center: [109.14354, 33.197863],
    zoom: 4,
    constraints: {
      minZoom: 3
    }
  })
  //添加图层
  webTileChange()
  map.add(graphicLayer)

  view.ui.remove("zoom")
  view.ui.remove("attribution")

  let list = new LayerList({view: view});
  view.ui.add(list, "bottom-left")

  //添加
  context.setView(view)
  view.when(() => {
    initListener()
    drawAirItem()
  })
  map.add(createFeature())
  initLegend()
}

const initLegend = () => {
  let grade = Grade.getPollutionLevel(RenderType.AQI)
  let id = document.getElementById("legend");
  if (id) {
    id.remove()
  }
  let legend = document.createElement("div");
  legend.id = "legend"
  let children = ""
  for (let gradeObject of grade) {
    children += `<div class='legend-flex'><div level="${gradeObject.level}" class='legend-item' style='background-color: ${gradeObject.color}'></div><span style="margin-left:8px">${gradeObject.label}</span></div>`
  }
  legend.innerHTML = children
  view.ui.add(legend, "top-left")
  const handle = (dom: PointerEvent) => {
    let target: any = dom.target;
    let level = target.getAttribute("level");
    graphicLayer.graphics.filter(item => {
      return item.getAttribute("level") == level
    }).forEach(res => {
      res.visible = !res.visible
      target.style.background = res.visible ? res.getAttribute("color") : "rgba(0,0,0,0.5)";
      return res
    })
  }
  for (let element of document.getElementsByClassName("legend-item")) {
    element.removeEventListener("click", handle)
    element.addEventListener("click", handle)
  }
}

const initCanvas = () => {
  imageCanvas.width = 2000
  imageCanvas.height = 2000
}

const createFeature = () => {
  return new GeoJSONLayer({
    title: "边界",
    url: "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json",
    renderer: new SimpleRenderer({
      symbol: new SimpleFillSymbol({
        color: "rgba(255,255,255,0)",
        outline: {width: 0.5, color: "#fff"}
      })
    })
  })
}

const initData = () => {
  initProvince()
}

const initProvince = () => {
  getProvinces().then(res => {
    provinces.value = res.data.data
  }).then(() => {
    initArea()
    initResizeListener()
    isUpdate()
  })
}

const initArea = () => {
  getAreas().then(res => {
    areas.value = res.data.data
  })
}

const isUpdate = () => {
  // getUpdate().then(() => {
  initRealTimeQuality()
  // })
}

const initRealTimeQuality = () => {
  // 获取实时空气质量
  choseAirQualityRealTime().then(() => {
    selectFirst()
  }).then(() => {
    initView()
  }).then(() => {
    loading.close()
  }).then(() => {
    refreshTime()
  })
}

const choseAirQualityRealTime = () => {
  getAreaRealTime().then(res => {
    realTimeQuality.value["1"] = areas.value.map(item => {
      return Object.assign(item, res.data.data.find(i => i.cityCode == item.cityCode));
    })
  })
  getProvinceRealTime().then(res => {
    realTimeQuality.value["0"] = provinces.value.map(item => {
      currentTime.value = res.data.data[0]?.timestamp
      return Object.assign(item, res.data.data.find(i => i.id == item.id));
    })
  })
  return getAirQualityRealTime().then(res => {
    realTimeQuality.value["2"] = res.data.data;
  })
}

const initListener = () => {
  const f = debounce(handleZoomChange, 500);
  handles.push(view.watch("zoom", () => {
    // 进行一个防抖
    f()
  }))
  //配置弹出
  handles.push(view.watch("popup", () => {
    view.popup.dockEnabled = true
    view.popup.dockOptions = {position: "bottom-right"}
    handles.push(view.popup.watch("selectedFeature", (selectedFeature: Graphic) => {
      if (selectedFeature) {
        //选中图层
        handleFeatureSelect(selectedFeature)
        view.goTo(selectedFeature)
      }
    }))
  }))
  handles.push(view.on("pointer-move", handlePinterMove))
}

const selectFirst = () => {
  let graphic = new Graphic();
  let attribute: Attribute = {
    aqi: 0, co: 0, color: "", id: 0, level: 0, no2: 0, o3: 0, pm10: 0, pm25: 0, so2: 0, title: "北京", type: 0
  }
  graphic.attributes = Object.assign(attribute, realTimeQuality.value["0"], provinces.value[0])
  handleFeatureSelect(currentFeature ?? graphic)
}

const removeListener = () => {
  for (let handle of handles) {
    handle.remove()
  }
}

/**
 * 绘制地图样式 多种绘制实现
 */
const initView = () => {
  //绘制点图层
  let render = strategyMap.get(currentStrategy.value);
  if (currentStrategy.value == "省区") {
    weather.value = null
  }
  context.setRender(render)
  context.data = realTimeQuality.value[render.type]
  context.render(renderType.value)
}

const map = new ArcGISMap({
  // basemap: "dark-gray"
})

const tileList = [
  {
    name: "ArcGIS蓝色底图",
    url: "https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}"
  },
  {
    name: "高德矢量底图",
    url: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}"
  },
  {
    name: "高德影像底图",
    url: "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}"
  },
  {
    name: "高德路网注记",
    url: "https://webst01.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}"
  },
  {
    name: "百度城市街道底图",
    url: "http://online01.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}"
  },
  {
    name: "百度道路和标记",
    url: "http://online01.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=sl"
  },
  {
    name: "百度卫星影像",
    url: "https://maponline01.bdimg.com/starpic/?qt=satepc&u=x={x};y={y};z={z};v=009;type=sate&fm=46"
  },
  {
    name: "ArcGIS卫星影像",
    url: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png"
  }
]

const currentWebTile = ref(tileList[0].url)

const webTileChange = () => {
  let layer = map.findLayerById("底图");
  layer && map.remove(layer);
  map.add(new WebTileLayer({
    title: "底图",
    urlTemplate: currentWebTile.value,
    // urlTemplate: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
    subDomains: ["1", "2", "3", "4"],
    opacity: 1,
    id: "底图",
  }),0)
}

/**
 * 地图视图
 */
let view: MapView;

const graphicLayer = new GraphicsLayer({title: "空气质量"})

const dict = ref<DictMixed>()

/**
 *  实时空气质量
 */
const realTimeQuality = ref<{
  2: Data[],
  1: AreaQuality[],
  0: (ProvinceQuality & Province)[]
}>({0: [], 1: [], 2: []})

/**
 * 全国省区数据
 */
const provinces = ref<Province[]>()

/**
 * 全国市区数据
 */
const areas = ref<Area[]>()

let context: Context = new Context(graphicLayer, map, null)

const loading = ElLoading.service({text: "加载中...", background: "rgba(0, 0, 0, 0.8)"})

const airItems = [["AQI", RenderType.AQI], ["PM2.5", RenderType.PM2_5], ["PM10", RenderType.PM10], ["SO2", RenderType.SO2], ["NO2", RenderType.NO2], ["CO", RenderType.CO], ["O3", RenderType.O3]]

const renderType = ref<RenderType>(RenderType.AQI)

const strategyMap = new Map<string, Render>([
  ["省区", new ProvinceRender()],
  ["市区", new AreaRender()],
  ["站点", new StationRender()]
])

const currentStrategy = ref<string>("省区")

const handles: IHandle[] = []

const timer: SelfEventListener = {
  interval: [], listener: [], time: []
}

const charStrategy = new Map<number, ChartStrategy>([
  [0, new ProvinceChartStrategy()],
  [1, new AreaChartStrategy()],
  [2, new StationChartStrategy()]
])

/**
 * 时间切换数据
 */
const timeExchange = ref<number>(0)

const timeList = ref<Time[]>()

let currentFeature: Graphic;

const currentFeatureTitle = ref()

const currentQuality = ref({
  aqi: 0,
  pm25: 0,
  pm10: 0,
  so2: 0,
  no2: 0,
  co: 0,
  o3: 0,
})

/**
 * 当前数据更新时间
 */
const currentTime = ref<string>()

/**
 * 底部按钮
 */
const footerMenu = ref<FooterData[]>([
  {
    method(): void {
      router.push({name: "index"})
    },
    name: "首页",
    icon: "HomeFilled"
  },
  {
    method: () => {
      router.push({name: "backend"})
    }, icon: "Management", name: "管理页面"
  },
  {
    method: () => {
      router.push({name: "data"})
    }, icon: "Search", name: "数据查询"
  },
  {
    method: () => {
      logout().then(() => {
        removeToken()
        ElMessage.success("退出成功")
      })
    },
    name: "退出",
    icon: "CloseBold"
  }
])

/**
 * 监测站信息
 */
const monitorData = ref({
  all: null,
  health: null,
  pollution: null,
  noData: null
})

const statusData = ref({
  interpolateDialog: false,
  messageDialog: false,
  baseMapDialog: false,
  mapDialog: false,
  map: {
    mapView: true,
    mapField: true,
    mapLegend: true,
  },
  progress: 0
})

const worker = new Worker(new URL('../../utils/kriging.js', import.meta.url), {type: "module"})

let moveGraphic: Graphic = null;

/**
 * 表单数据
 */
//@ts-ignore
const interpolateForm = ref<BaseMixed>({
  type: null,
  width: 10000
})

const interpolateRef = ref<FormInstance>()

let imageLayer: BaseDynamicLayer = null;

const weather = ref<Weather>()

const imageCanvas: HTMLCanvasElement = document.createElement("canvas");

const mapExtent = {
  xmin: 8182244.730706286,
  xmax: 15038781.192775872,
  ymin: 378392.10080518306,
  ymax: 7087874.746169141
}

const processList = ref({
  process: ProcessManager.getProcesses(),
  error: ProcessManager.getError(),
  success: ProcessManager.getSuccess(),
  history: ProcessManager.getHistory()
})

const handleAreaSearch = () => {
  console.log("search")
}

/**
 * 整合后的搜索列表
 */
const searchList = ref([{desc: "地区搜索", method: handleAreaSearch}])

/**
 * 空气质量字段样式
 */
const drawAirItem = () => {
  let elementById = document.getElementById("airGroup");
  if (elementById) {
    elementById.remove()
  }
  let item = document.createElement("div")
  item.id = "airGroup"
  let html = ""
  for (let airItem of airItems) {
    html += `<div class="airItem ${airItem[1] == renderType.value ? 'active' : ''}" item="${airItem[1]}">${airItem[0]}</div>`
  }
  item.innerHTML = html
  view.ui.add(item, "top-right")
  for (let element of document.getElementsByClassName("airItem")) {
    element.addEventListener("click", handleAirItemChange)
    timer.listener.push({func: handleAirItemChange, event: "click", userCapture: element})
  }
}

/**
 * 切换空气质量渲染字段
 * @param element
 */
const handleAirItemChange = (element) => {
  let data: HTMLDivElement = element.target;
  //切换当前的渲染字段
  let attribute = data.getAttribute("item") as RenderType;
  if (attribute == renderType.value) return
  renderType.value = attribute
  //移除所有active
  removeClass("airItem", "active")
  //添加点击的元素高亮
  data.classList.add("active")
  // 重新渲染
  initView()
  handleRenderData(RenderChange.FieldChange)
}

/**
 * 将指定元素类的classList移除某个属性
 */
const removeClass = (element: string, className: string) => {
  for (let target of document.getElementsByClassName(element)) {
    target.classList.remove(className)
  }
}

const handleZoomChange = () => {
  switch (currentStrategy.value) {
    case "省区":
      break
    case "市区":
      context.reRender()
      break
    case "站点":
      context.reRender()
      break
  }
}

const handleLevelChange = (str: string) => {
  if (currentStrategy.value == str) return
  currentStrategy.value = str
  initView()
}

const handleFeatureSelect = (feature: Graphic) => {
  if (currentFeature == feature) return
  currentFeature = feature
  currentFeatureTitle.value = feature.attributes.title
  currentQuality.value = Common.objectAssign(currentQuality.value, currentFeature.attributes)
  handleRenderData(RenderChange.TargetChange)
  handleWeather()
}

const handleWeather = () => {
  if (currentFeature.attributes.type != 0) {
    getWeather({id: currentFeature.attributes.id, type: currentFeature.attributes.type}).then(res => {
      weather.value = res.data.data
    })
  }
}

const handleTimeChange = (item: number) => {
  if (timeExchange.value == item) return
  timeExchange.value = item
  handleRenderData(RenderChange.TimeChange)
}

const handleRenderData = (change: RenderChange) => {
  let type = currentFeature.attributes.type;
  context.setChart = charStrategy.get(type)
  context.renderData(currentFeature.attributes, timeExchange.value, renderType.value, realTimeQuality.value[type], change)
}

const refreshTime = () => {
  const now = new Date()
  let minutes = now.getMinutes();
  timer.time.push(setTimeout(isUpdate, 60 * 1000 * ((minutes < 23 ? 23 : 83) - minutes)))
}


watch(() => realTimeQuality, () => {
  let data = realTimeQuality.value;
  let map = data["2"].map(item => {
    let parseFloat = Number.parseFloat(item?.stationQuality?.aqi as string);
    return Number.isNaN(parseFloat) ? 0 : parseFloat;
  });
  let AQI = Grade.getPollutionLevel(RenderType.AQI)
  let allAqi = map.filter(item => item > 0).map(item => Grade.getPollutionLevelByValue(item, AQI).level);
  let noData = map.filter(item => item == 0).length;
  monitorData.value = {
    all: data["2"].length,
    noData: noData,
    pollution: allAqi.filter(item => item > 2).length,
    health: allAqi.filter(item => item <= 2).length
  }
}, {immediate: true, deep: true})

const handlePinterMove = (event) => {
  view.hitTest(event).then(res => {
    let filter = res.results.filter(item => item.layer == graphicLayer);
    if (filter.length > 0) {
      handleGraphicMouse(res)
      moveGraphic = null
    } else {
      document.body.style.cursor = "auto"
      handleAnimationScalarEnd(0)
      moveGraphic = null
    }
  })
}

const handleGraphicMouse = async (e: HitTestResult) => {
  // 需要获取几个数据 第一个图形数据 第二个位置信息
  let graphic = e.results.pop() as GraphicHit
  if (graphic.graphic == moveGraphic) {
    return
  }
  handleAnimationScalarStart(graphic.graphic, 0)
  document.body.style.cursor = "pointer"
}

const handleInterpolate = () => {
  if (currentStrategy.value == "省区") {
    ElMessage.warning("省区模式下无法进行插值")
    return
  }
  statusData.value.interpolateDialog = true
}

/**
 * 普通克里金实现
 */
const handleKriging = (obj: RootObject, process: ProcessFace) => {
  process.process("插值数据准备中")
  let level = obj.param.field=="aqi"?"level":obj.param.field+"Level";
  let graphics = graphicLayer.graphics.filter(item => {
    return item.geometry instanceof Point && item.attributes[level] > 0
  }).map(item => {
    let geometry = webMercatorUtils.geographicToWebMercator(item.geometry) as Point
    return {
      z: item.attributes[level],
      x: geometry.x,
      y: geometry.y
    }
  }).toArray()
  process.process("插值数据准备完成")
  handleWorkerPost({
    data: graphics,
    params: {
      model: dict.value.kriging.filter(item => {
        return item.code == interpolateForm.value.model
      })[0].desc,
      sigma2: interpolateForm.value.sigma2,
      alpha: interpolateForm.value.alpha
    }
  }, 0, process, obj)
}

const handleIdw = (obj, process: ProcessFace) => {
  process.process("插值数据准备中")
  let level = obj.param.field=="aqi"?"level":obj.param.field+"Level";
  let data = graphicLayer.graphics.filter(item => {
    return item.geometry instanceof Point && item.attributes[level] > 0
  }).map(item => {
    let geometry = webMercatorUtils.geographicToWebMercator(item.geometry) as Point
    return {
      values: item.attributes[level],
      positions: [geometry.x, geometry.y],
    }
  }).toArray()
  process.process("插值数据准备完成")
  handleWorkerPost({
    data: data,
    distance: dict.value.idw.filter(item => {
      return item.code == interpolateForm.value.distance
    })[0].desc,
    power: interpolateForm.value.power,
  }, 1, process, obj)
}

const workerMessage = {
  //负责grid计算
  0: (e: MessageEvent) => {
    let {gridInfo, obj, id} = e.data
    handlePlot(gridInfo, obj, handlePostKriging, ProcessManager.getProcess(id))
  },
  1: (e: MessageEvent) => {
    let {gridInfo, obj, id} = e.data
    handlePlot(gridInfo, obj, handlePostIdw, ProcessManager.getProcess(id))
  },
  2: (e: MessageEvent) => {
    let {id, info, method} = e.data
    ProcessManager.getProcess(id)[method](info)
  }
}

const handleWorkerPost = (args: any, status: number, process: ProcessFace, obj?: RootObject) => {
  worker.postMessage({...args, status: status, obj, pipList, id: process.id, width: interpolateForm.value.width})
}

const handleDraw = (url: string, process: ProcessFace) => {
  if (imageLayer) {
    map.remove(imageLayer)
  }
  //为了触发更新
  imageLayer = new CustomImageOverlayLayer({
    title: "插值",
    picUrl: url,
    canvas: imageCanvas, extent: new Extent({
      xmin: mapExtent.xmin,
      xmax: mapExtent.xmax,
      ymin: mapExtent.ymin,
      ymax: mapExtent.ymax,
    })
  })
  process.finish("插值分析完成")
  map.add(imageLayer)
}

const handlePlot = (gridInfo, obj, method: Function, process: ProcessFace) => {
  let level = Grade.getPollutionLevel(RenderType.AQI);
  let breaks = level
      .filter(item => {
        return item.level >= gridInfo.zlim[0] && item.level <= gridInfo.zlim[1]
      })
      .map(item => item.color);
  process.process("开始绘制图像")
  plot(imageCanvas, gridInfo, gridInfo.xlim, gridInfo.ylim, breaks)
  imageCanvas.toBlob((blob) => {
    let url = URL.createObjectURL(blob);
    method(blob, obj)
    handleDraw(url, process)
  })
}

//自定义叠加图片图层 绘制图片
//@ts-ignore
let CustomImageOverlayLayer = BaseDynamicLayer.createSubclass({
  properties: {
    picUrl: null,
    extent: null,
    image: null,
    canvas: null,
  },
  //@ts-ignore
  getImageUrl: function (extent, width, height) {
    //重新更新
    if (this.image?.src != this.picUrl) {
      this.image = new Image()
      this.image.src = this.picUrl;
    }

    // 创建canvas DOM元素，并设置其宽高和图片一样
    if (!this.canvas) {
      this.canvas = document.createElement("canvas");
      this.canvas.width = this.image.width;
      this.canvas.height = this.image.height;
    }

    //左上角地理坐标转换屏幕坐标,为了获取canvas绘制图片的起点
    let mapPoint = new Point({
      x: this.extent.xmin,
      y: this.extent.ymax,
      spatialReference: view.spatialReference
    });
    let screenPoint = view.toScreen(mapPoint as Point);
    //根据extent范围计算canvas绘制图片的宽度以及高度
    //左下角
    let leftbottom = new Point({
      x: this.extent.xmin,
      y: this.extent.ymin,
      spatialReference: view.spatialReference
    });
    let screen_leftbottom = view.toScreen(leftbottom as Point);
    //右上角
    let righttop = new Point({
      x: this.extent.xmax,
      y: this.extent.ymax,
      spatialReference: view.spatialReference
    });
    let screen_righttop = view.toScreen(righttop);
    let context: CanvasRenderingContext2D = this.canvas.getContext("2d")
    let dw = Math.abs(screen_righttop.x - screenPoint.x);
    let dh = Math.abs(screen_leftbottom.y - screenPoint.y);
    context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    return new Promise((resolve) => {
      if (this.image.complete) {
        context.drawImage(this.image, screenPoint.x, screenPoint.y, dw, dh);
        resolve(this.canvas.toDataURL("image/png", 1.0))
      }
      this.image.onload = () => {
        context.drawImage(this.image, screenPoint.x, screenPoint.y, dw, dh);
        resolve(this.canvas.toDataURL("image/png", 1.0))
      }
    })
  }
});

/**
 * 为鼠标悬浮添加动画
 * @param graphic
 * @param at
 */
const handleAnimationScalarStart = (graphic: Graphic, at: number) => {
  if (graphic.geometry instanceof Point) {
    let screen = view.toScreen(graphic.geometry as Point); //屏幕位置
    let attributes: Attribute = graphic.attributes;
    let element = document.getElementsByClassName("animation-scalar")[at] as HTMLElement;
    element.style.display = "block"

    let first = element.children.item(0) as HTMLElement;
    let second = element.children.item(1) as HTMLElement;

    let size = (graphic.symbol as SimpleMarkerSymbol).size + "pt";
    let s = -((graphic.symbol as SimpleMarkerSymbol).size / 2) + "pt";

    first.style.height = size
    first.style.width = size

    second.style.height = size
    second.style.width = size
    first.style.top = s
    first.style.left = s
    first.style.backgroundColor = attributes.color;


    second.style.top = s
    second.style.left = s
    second.style.backgroundColor = attributes.color;
    //设置动画
    element.style.top = screen.y + "px";
    element.style.left = screen.x + "px";
  }
}

const handleAnimationScalarEnd = (at: number) => {
  let element = document.getElementsByClassName("animation-scalar")[at] as HTMLElement;
  element.style.display = "none"
}

/**
 * 执行插值分析
 */
const handleInterpolateExe = () => {
  //执行第一步 查询是否已经存在
  interpolateRef.value.validate(isValid => {
    if (isValid) {
      handleInterpolateSearch()
    }
  })
}

/**
 * 执行插值图像搜索
 */
const handleInterpolateSearch = () => {
  let process = ProcessManager.addProcess("插值分析");
  switch (interpolateForm.value.type) {
    case 0:
      process.start("开始进行克里金插值图像检索")
      handleKrigingSearch(process)
      break
    case 1:
      process.start("开始进行(反距离插值)IDW插值图像检索")
      handleIdwSearch(process)
      break
    default:
      process.error()
  }
}

const generateBaseRootObject = () => {
  return {
    param: {
      field: renderType.value,
      range: currentStrategy.value == "市区" ? 0 : 1,
      width: interpolateForm.value.width
    },
    time: currentTime.value,
    type: interpolateForm.value.type
  }
}

/**
 * 执行克里金插值搜索
 */
const handleKrigingSearch = (process: ProcessFace) => {
  let base = generateBaseRootObject();
  let obj: RootObject = Common.deepMerge({}, base, {
    param: {
      alpha: interpolateForm.value.alpha,
      model: interpolateForm.value.model,
      sigma2: interpolateForm.value.sigma2,
    },
  })
  searchInterpolate(obj).then(res => {
    if (res.data.msg) {
      process.process("图像检索完成，开始进行图像渲染")
      handleDraw("src/assets" + res.data.msg, process)
    } else {
      process.process("图像检索失败，开始进行插值分析")
      handleKriging(obj, process)
    }
    statusData.value.interpolateDialog = false
  })
}

const handleIdwSearch = (process: ProcessFace) => {
  let base = generateBaseRootObject();
  let obj: RootObject = Common.deepMerge({}, base, {
    param: {
      power: interpolateForm.value?.power,
      distance: interpolateForm.value?.distance,
    },
  })
  searchInterpolateIdw(obj).then(res => {
    if (res.data.msg) {
      process.process("图像检索完成，开始进行图像渲染")
      handleDraw("src/assets" + res.data.msg, process)
    } else {
      process.process("图像检索失败，开始进行插值分析")
      handleIdw(obj, process)
    }
    statusData.value.interpolateDialog = false
  })
}

/**
 * 将插值结果上传
 * @param blob
 * @param obj
 * @param postFunction 上传方法
 */
const handlePost = (blob: Blob, obj: RootObject, postFunction: Function) => {
  let file = new File([blob], "ww.png", {type: "image/png"});

  let formData = new FormData();
  formData.set("post", new Blob([JSON.stringify(obj)], {type: "application/json"}))
  formData.set("file", file)
  postFunction(formData)
}

const handlePostKriging = (blob: Blob, obj: RootObject) => {
  handlePost(blob, obj, postKrigingInterpolate)
}

const handlePostIdw = (blob: Blob, obj: RootObject) => {
  handlePost(blob, obj, postIdwInterpolate)
}

const handlePipList = (polygons: number[][][]): {
  rangeList: { lxlim: number[], lylim: number[] }[],
  xlim: number[],
  ylim: number[]
} => {
  const n = polygons.length;
  const xlim = [polygons[0][0][0], polygons[0][0][0]];
  const ylim = [polygons[0][0][1], polygons[0][0][1]];
  let rangeList = Array(n)
  for (let i = 0; i < n; i++) {
    let lxlim = Array(2)
    let lylim = Array(2)
    lxlim[0] = polygons[i][0][0];
    lxlim[1] = lxlim[0];
    lylim[0] = polygons[i][0][1];
    lylim[1] = lylim[0];
    for (let j = 0; j < polygons[i].length; j++) { // Vertices
      if (j > 0) {
        if (polygons[i][j][0] < lxlim[0]) lxlim[0] = polygons[i][j][0];
        if (polygons[i][j][0] > lxlim[1]) lxlim[1] = polygons[i][j][0];
        if (polygons[i][j][1] < lylim[0]) lylim[0] = polygons[i][j][1];
        if (polygons[i][j][1] > lylim[1]) lylim[1] = polygons[i][j][1];
      }
      if (polygons[i][j][0] < xlim[0]) xlim[0] = polygons[i][j][0];
      if (polygons[i][j][0] > xlim[1]) xlim[1] = polygons[i][j][0];
      if (polygons[i][j][1] < ylim[0]) ylim[0] = polygons[i][j][1];
      if (polygons[i][j][1] > ylim[1]) ylim[1] = polygons[i][j][1];
    }
    //构建判断函数
    rangeList[i] = {lxlim, lylim}
  }
  return {rangeList, xlim, ylim}
}

const pipList = handlePipList(interpolatePoly)

const handleInterpolateSelect = (value) => {
  switch (value) {
      //kriging
    case 0:
      interpolateForm.value = Object.assign({}, interpolateForm.value, {
        model: 1,
        alpha: 100,
        sigma2: 0,
      })
      break
      //idw
    case 1:
      interpolateForm.value = Object.assign({}, interpolateForm.value, {
        power: 20,
        distance: 1
      })
      break
  }
}

const handleDialogClose = () => {
  //@ts-ignore
  interpolateForm.value = {width: 10000, type: null}
}

const handleMapView = () => {
  let element = document.getElementsByClassName("esri-layer-list")[0] as HTMLDivElement;
  element.style.display = statusData.value.map.mapView ? "flex" : "none"
}

const handleMapLegend = () => {
  let element = document.getElementById("legend") as HTMLDivElement;
  element.style.display = statusData.value.map.mapLegend ? "block" : "none"
}

const handleMapField = () => {
  let element = document.getElementById("airGroup") as HTMLDivElement;
  element.style.display = statusData.value.map.mapField ? "block" : "none"
}
</script>
<style scoped>
.drop {
  vertical-align: middle;
}

#map {
  height: 100%;
}

.main {
  padding: 0 8px 2px 8px;
}

.main-map {
  background: url("../../assets/echarts-back.png") no-repeat;
  position: absolute;
  inset: 0 0 0 0;
  pointer-events: none;
  background-size: 100% 100%;
}

.header {
  padding: 0;
  position: relative;
}

.header-top {
  background: url("../../assets/header-top.png") no-repeat center;
  background-size: 100% 100%;
}

.header-bottom-item {
  color: #9FCEFF;
  margin: auto 0;
}

.header-bottom-item:focus-visible {
  outline: none;
}


.header-bottom-item:hover {
  cursor: pointer;
}

.header-item {
  font-size: xx-large;
  color: #02A9FF;
  padding: 0;
}

.header-bottom {
  background: url("../../assets/header-bottom.png") no-repeat center;
  background-size: 100% 100%;
  height: 50px;
  margin: auto;
  width: 100%
}

.grid-header {
  display: grid;
  grid-template-columns: 33% 34%  33%;
}

.container-back {
  position: fixed;
  inset: 0 0 0 0;
  z-index: -2;
  background-image: url("../../assets/back.png");
}

.header-back {
  padding: 0;
  position: absolute;
  height: 100%;
  left: 0;
  right: 0;
  z-index: -1;
  top: 0;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-image: url("../../assets/header-img.png");
}

.esri-view-surface {
  &:after {
    display: none;
  }
}

.height-100 {
  height: 100%;
}

.text {
  color: #9FCEFF;
}

.echarts {
  /* overflow: hidden; */
}

.echarts-item-pre {
  background: url("../../assets/rect.png") no-repeat;
  background-size: 100% 100%;
  display: inline-block;
  height: 0.75em;
  width: 1em;
}

.padding-left-4 {
  padding-left: 4px;
}

.padding-left-8 {
  padding-left: 8px;
}

.padding-right-4 {
  padding-right: 4px;
}

.grid-space {
  display: grid;
  grid-template-rows: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
  grid-template-columns: 100%;
  grid-gap: 2px;
}

.container {
  height: calc(100% - 100px);
}

.grid-map {
  display: grid;
  grid-template-rows: 70%;
  grid-template-columns: 100%;
  grid-row-gap: 4px;
}

.aqi-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  column-gap: 6px;
  margin: 0 6px;
}

.echarts-pie {
  display: flex;
  position: relative;
  justify-content: center;
}

.echarts-pie > .echarts {
  flex: 0 0 50%;
}

.pie-line {
  position: absolute;
  top: 30%;
  width: 85%;
  border-bottom: 1px solid rgba(255, 255, 255, .5);
}

.station-flex {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.header-info-item {
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  padding: 2px 12px;
  color: #fff;
  font-size: 1vw;
}

.header-info-item:focus-visible {
  outline: none;
}

.station-text {
  color: #fff;
}

.echarts-station {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
}

.station-number {
  border-radius: 20px;
  padding: 8px 15px;
  line-height: 1;
  color: #fff;
  font-size: 1.2vw;
  margin-bottom: 4px;
}

.station-number-1 {
  background-color: #9FCEFF;
}

.station-number-2 {
  background-color: var(--el-color-danger);
}

.station-number-3 {
  background-color: var(--el-color-success);
}

.station-number-4 {
  background-color: var(--el-color-warning);
}

.animation-scalar {
  position: absolute;
  width: 10px;
  height: 10px;
  pointer-events: none;
  display: none;
}

.wave1 {
  animation: scale3 2s infinite;
  width: 10px;
  height: 10px;
  position: absolute;
  border-radius: 50%;
}

.wave2 {
  animation: scale4 2s infinite;
  width: 10px;
  height: 10px;
  position: absolute;
  border-radius: 50%;
}

.header-info {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  align-items: center;
}

@keyframes scale3 {
  0% {
    transform: scale(1);
    opacity: .9;
  }

  100% {
    transform: scale(5);
    opacity: 0;
  }
}

@keyframes scale4 {
  0% {
    transform: scale(1);
    opacity: .9;
  }

  100% {
    transform: scale(8);
    opacity: 0;
  }
}

.header-drop {
  padding: 7px 12px;
}

.cursor:hover {
  cursor: pointer;
}

.header-health {
  color: #FFF;
  font-size: 1vw;
  display: flex;
  justify-content: center;
  align-items: start;
  width: 50%;
}

:root {
  --el-font-size-base: 1vw;
}
</style>
<style>
.drop-menu {
  --el-bg-color-overlay: #07183e;
  --el-border-color-light: #02A9FF;
  --el-text-color-regular: #9FCEFF;
  --el-dropdown-menuItem-hover-fill: #07183e;
  --el-dropdown-menuItem-hover-color: rgb(234, 166, 81);
}

#airGroup {
  padding: 4px;
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  transition: all 0.7s;
}

.active {
  color: #535bf2;
}

.el-dropdown-menu__item:not(.is-disabled):focus {
  background-color: rgba(0, 0, 0, 0);
}

.airItem:hover {
  cursor: pointer;
}

.legend-flex {
  transition: all 0.7s;
  border-radius: 4px;
  display: flex;
  color: #fff;
  margin-top: 2px;
  justify-content: start;
  align-items: center;
}

#legend {
  padding: 8px;
  background-color: rgba(0, 0, 0, 0.5);
}

.legend-item {
  width: 2.3vw;
  height: 1.3vw;
  border-radius: 4px;
  transition: all 0.7s;
  font-size: large;
}

.legend-item:hover {
  cursor: pointer;
}


#airGroup {
  font-size: 0.9vw;
}

#legend {
  font-size: 0.9vw;
}
</style>